/*
-------------------------------------------------

	Plane.h

	Dmitriy Kuzmenko (Dgut)

-------------------------------------------------
*/

#ifndef __AUTUMN_PLANE_H__
#define __AUTUMN_PLANE_H__

namespace Autumn
{

template< typename T, int N >
class Vector;

template< typename T, int N >
class Plane
{
public:
	Vector< T, N >						normal;
	T									distance;

	inline								Plane( void );
	inline								Plane( const Plane< T, N > & plane );
	inline								Plane( const Vector< T, N > & normal, T distance );
	inline								Plane( const Vector< T, N > point, ... );

	inline const Plane< T, N >			Normalize( void ) const;
	inline const T						Distance( const Vector< T, N > & vector ) const;

	inline const bool					operator ==( const Plane< T, N > & plane ) const;
	inline const bool					operator !=( const Plane< T, N > & plane ) const;

	inline Plane< T, N > &				operator =( const Plane< T, N > & plane );
	inline Plane< T, N > &				operator *=( T value );
	inline Plane< T, N > &				operator /=( T value );

	inline const Plane< T, N >			operator -( void ) const;

	inline const Plane< T, N >			operator *( T value ) const;
	inline const Plane< T, N >			operator /( T value ) const;

	template< typename T, int N >
	friend inline const Plane< T, N >	operator *( T value, const Plane< T, N > & plane );

	inline T &							operator []( int index );
	inline const T &					operator []( int index ) const;
};

//typedef Plane< float > planef;

template< typename T >
inline const Plane< T, 2 > CreatePlane( const Vector< T, 2 > & a, const Vector< T, 2 > & b )
{
	return Plane< T, 2 >( Vector< T, 2 >( b[ 1 ] - a[ 1 ], a[ 0 ] - b[ 0 ] ), -a[ 0 ] * ( b[ 1 ] - a[ 1 ] ) + a[ 1 ] * ( b[ 0 ] - a[ 0 ] ) );
}

template< typename T, int N >
inline Plane< T, N >::Plane( void ) :
	distance	( 0 )
{
}

template< typename T, int N >
inline Plane< T, N >::Plane( const Plane< T, N > & plane ) :
	normal		( plane.normal ),
	distance	( plane.distance )
{
}

template< typename T, int N >
inline Plane< T, N >::Plane( const Vector< T, N > & normal, T distance ) :
	normal		( normal ),
	distance	( distance )
{
}

template< typename T, int N >
inline Plane< T, N >::Plane( const Vector< T, N > point, ... )
{
	if( N == 2 )
	{
		normal = Vector< T, N >( ( &point )[ 1 ][ 1 ] - ( &point )[ 0 ][ 1 ], ( &point )[ 0 ][ 0 ] - ( &point )[ 1 ][ 0 ] );
		distance = -( &point )[ 0 ][ 0 ] * ( ( &point )[ 1 ][ 1 ] - ( &point )[ 0 ][ 1 ] ) + ( &point )[ 0 ][ 1 ] * ( ( &point )[ 1 ][ 0 ] - ( &point )[ 0 ][ 0 ] );
	}
}

template< typename T, int N >
inline const Plane< T, N > Plane< T, N >::Normalize( void ) const
{
	const T L = normal.Length();

	if( !L || L == 1 )
		return *this;

	return Plane( normal / L, distance / L );
}

template< typename T, int N >
inline const T Plane< T, N >::Distance( const Vector< T, N > & vector ) const
{
	return ( normal * vector + distance ) / normal.Length();
}

template< typename T, int N >
inline const bool Plane< T, N >::operator ==( const Plane< T, N > & plane ) const
{
	return normal == plane.normal && distance == plane.distance;
}

template< typename T, int N >
inline const bool Plane< T, N >::operator !=( const Plane< T, N > & plane ) const
{
	return !( *this == plane );
}

template< typename T, int N >
inline Plane< T, N > & Plane< T, N >::operator =( const Plane< T, N > & plane )
{
	normal = plane.normal;
	distance = plane.distance;

	return *this;
}

template< typename T, int N >
inline Plane< T, N > & Plane< T, N >::operator *=( T value )
{
	normal *= value;
	distance *= value;

	return *this;
}

template< typename T, int N >
inline Plane< T, N > & Plane< T, N >::operator /=( T value )
{
	normal /= value;
	distance /= value;

	return *this;
}

template< typename T, int N >
inline const Plane< T, N > Plane< T, N >::operator -( void ) const
{
	return Plane( *this ) *= -1;
}

template< typename T, int N >
inline const Plane< T, N > Plane< T, N >::operator *( T value ) const
{
	return Plane( *this ) *= value;
}

template< typename T, int N >
inline const Plane< T, N > Plane< T, N >::operator /( T value ) const
{
	return Plane( *this ) /= value;
}

template< typename T, int N >
inline const Plane< T, N > operator *( T value, const Plane< T, N > & plane )
{
	return *this * value;
}

template< typename T, int N >
inline T & Plane< T, N >::operator []( int index )
{
	return *( static_cast< T * >( this ) + index );
}

template< typename T, int N >
inline const T & Plane< T, N >::operator []( int index ) const
{
	return *( static_cast< T * >( this ) + index );
}

}	// namespace Autumn

#endif	// !!! __AUTUMN_PLANE_H__ !!!